use std::path::{Path, PathBuf};
use std::fs;
use tokio::sync::Semaphore;
use std::sync::Arc;
use futures::future::join_all;
use tracing::error;
use crate::error::{Result, ConversionError};
use crate::api::ApiClient;
use crate::config::settings::{Settings, USER_PROMPT_TEMPLATE};
use crate::utils::format::clean_code;

pub struct CodeConverter<T: ApiClient + Send + Sync + 'static> {
    settings: Arc<Settings>,
    api_client: Arc<T>,
    semaphore: Arc<Semaphore>,
}

impl<T: ApiClient + Send + Sync + 'static> CodeConverter<T> {
    pub fn new(settings: Settings, api_client: T) -> Self {
        let settings = Arc::new(settings);
        let parallel_jobs = settings.conversion.parallel_jobs as usize;
        Self {
            semaphore: Arc::new(Semaphore::new(parallel_jobs)),
            api_client: Arc::new(api_client),
            settings,
        }
    }

    fn collect_input_files(&self) -> Result<Vec<PathBuf>> {
        let files: Vec<_> = fs::read_dir(&self.settings.paths.input_dir)?
            .filter_map(|entry| entry.ok())
            .filter(|entry| {
                entry.path().extension()
                    .map_or(false, |ext| ext == "c")
            })
            .map(|entry| entry.path())
            .collect();

        Ok(files)
    }

    async fn process_file(&self, file_path: PathBuf) -> Result<()> {
        let _permit = self.semaphore.clone().acquire_owned().await.map_err(|e| {
            ConversionError::Conversion(format!("Failed to acquire semaphore: {}", e))
        })?;

        let target_file = self.settings.paths.output_dir
            .join(file_path.file_stem().unwrap())
            .with_extension("rs");

        if !target_file.exists() {
            println!("\nProcessing {}...", file_path.display());
            let c_code = fs::read_to_string(&file_path)?;
            
            let rust_code = self.api_client.generate_code(&c_code, USER_PROMPT_TEMPLATE).await?;
            
            if rust_code.trim().is_empty() {
                return Err(ConversionError::Conversion(
                    format!("Generated code is empty for file: {}", file_path.display())
                ));
            }
            
            let cleaned_code = clean_code(&rust_code);
            
            if cleaned_code.trim().is_empty() {
                return Err(ConversionError::Conversion(
                    format!("Cleaned code is empty for file: {}", file_path.display())
                ));
            }
            
            println!("Generated code length: {} bytes", cleaned_code.len());
            
            fs::write(&target_file, cleaned_code)?;
            println!("Code written to: {}", target_file.display());
        } else {
            println!("\n{} already exists, skipping...", target_file.display());
        }

        Ok(())
    }

    pub async fn convert_all(&self) -> Result<()> {
        let files = self.collect_input_files()?;
        if files.is_empty() {
            println!("No C files found");
            return Ok(());
        }

        println!("Found {} C files to process", files.len());

        let tasks: Vec<_> = files.into_iter()
            .map(|file| {
                let this = self.clone();
                tokio::spawn(async move {
                    if let Err(e) = this.process_file(file.clone()).await {
                        error!("Failed to process file {}: {}", file.display(), e);
                    }
                })
            })
            .collect();

        for result in join_all(tasks).await {
            if let Err(e) = result {
                error!("Task failed: {}", e);
            }
        }

        println!("\nProcessing complete!");
        Ok(())
    }
}

impl<T: ApiClient + Send + Sync + 'static> Clone for CodeConverter<T> {
    fn clone(&self) -> Self {
        Self {
            settings: self.settings.clone(),
            api_client: self.api_client.clone(),
            semaphore: self.semaphore.clone(),
        }
    }
} 